#include "http_client.h"
#include "string.h"

HttpClientDriver* main_http_client_driver;
thread_local HttpClientDriver* HttpClient;

// static int http_request_decode(HttpRequest* req, char* data, size_t len) {
// 	size_t plen = 0;
// 	char* p1 = data, *p2, *m, *n;

// 	HttpHeader::iterator iter = req->header.find("Transfer-Encoding");
// 	if (iter != req->header.end() && iter->second == "chunked") {
// 		goto chunked;
// 	}

// 	/*metho*/
// 	p2 = strchr(p1, ' ');
// 	if (!p2) return 0;
// 	req->method = std::string(p1, p2 - p1);
// 	p1 = p2 + 1;
// 	/*uri*/
// 	p2 = strchr(p1, ' ');
// 	if (!p2) return 0;
// 	*p2 = 0x0;
// 	m = strchr(p1, '?');
// 	*p2 = ' ';
// 	if (m) {
// 		req->uri = std::string(p1, m - p1);
// 		m++;
// 		req->query = std::string(m, p2 - m);
// 	}
// 	else{
// 		req->uri = std::string(p1, p2 - p1);
// 	}
// 	p1 = p2 + 1;
// 	/*version*/
// 	p2 = strstr(p1, "\r\n");
// 	if (!p2) return 0;
// 	req->version = std::string(p1, p2 - p1);
// 	p1 = p2 + 2;
// 	/*header*/
// 	while(1) {
// 		p2 = strstr(p1, "\r\n");
// 		if (!p2) return 0;
// 		*p2 = 0x0;
// 		m = strchr(p1, ':');
// 		*p2 = '\r';
// 		if (!m) break;
// 		*m = 0x0;
// 		n = m + 1;
// 		while (*n == ' '){
// 			n++;
// 		}
// 		req->header[p1] = std::string(n, p2 - n);
// 		*m = ':';
// 		p1 = p2 + 2;
// 	}
// 	p1 = p2 + 2;
// 	/*content*/
// 	iter = req->header.find("Transfer-Encoding");
// 	if (iter != req->header.end() && iter->second == "chunked") {
// chunked:
// 		while (true){
// 			p2 = strstr(p1, "\r\n");
// 			if (!p2) {
// 				plen = p1 - data;
// 				goto end;
// 			}
// 			size_t clen = strtol(p1, &p2, 16);
// 			if (clen == 0) {
// 				plen = p2 + 4 - data;
// 				goto end;
// 			} 
// 			else if (len < p1 - data + clen) {
// 				plen = p1 - data;
// 				goto end;
// 			} 
// 			else {
// 				p1 = p2 + 2;
// 				req->content.append(p1, clen);
// 				p1 += clen;
// 			}
// 		}
// 	}
// 	else {
// 		iter = req->header.find("Content-Length");
// 		int clen = 0;
// 		if (iter != req->header.end()) {
// 			clen = atoi(iter->second.c_str());
// 		}
// 		plen = p1 - data + clen;
// 		if (len < plen) return 0;
// 		req->content = std::string(p1, clen);
// 		goto end;
// 	}
// end:
// 	return (int)plen;
// }

static int http_response_decode(HttpResponse* res, char* data, size_t len) {
	size_t plen = 0;
	char* p1 = data, * p2, * m, * n;
	/*version*/
	p2 = strchr(p1, ' ');
	if (!p2) return 0;
	res->version = std::string(p1, p2 - p1);
	p1 = p2 + 1;
	/*state_code*/
	p2 = strchr(p1, ' ');
	if (!p2) return 0;
	res->state_code = atoi(p1);
	p1 = p2 + 1;
	/*state*/
	p2 = strstr(p1, "\r\n");
	if (!p2) return 0;
	res->state = std::string(p1, p2 - p1);
	p1 = p2 + 2;
	/*header*/
	while (1) {
		p2 = strstr(p1, "\r\n");
		if (!p2) return 0;
		if (p2 == p1) break;
		m = strchr(p1, ':');
		if (!m || m > p2) continue;
		*m = 0x0;
		n = m + 1;
		while (*n == ' ') {
			n++;
		}
		res->header[p1] = std::string(n, p2 - n);
		*m = ':';
		p1 = p2 + 2;
	}
	p1 = p2 + 2;
	/*content*/
	HttpHeader::iterator iter = res->header.find("Content-Length");
	int clen = 0;
	if (iter != res->header.end()) {
		clen = atoi(iter->second.c_str());
	}
	plen = p1 - data + clen;
	if (len < plen) return 0;
	res->content = std::string(p1, clen);
	return (int)plen;
}

static std::string http_request_encode(HttpRequest* req) {
	std::string data;
	if (req->query.empty()) {
		data += req->method + " " + req->uri + " "+ req->version + "\r\n";
	}
	else {
		data += req->method + " " + req->uri + "?" + req->query + " " + req->version + "\r\n";
	}
	req->header["Content-Length"] = std::to_string(req->content.size());
	HttpHeader::iterator iter;
	for (iter = req->header.begin(); iter != req->header.end(); ++iter) {
		data += iter->first + ": " + iter->second + "\r\n";
	}
	data += "\r\n" + req->content;
	return data;
}

// static std::string http_response_encode(HttpResponse* res) {
// 	std::string data;
// 	data += res->version + " " + std::to_string(res->state_code) + " " + res->state + "\r\n";
// 	res->header["Content-Length"] = std::to_string(res->content.size());
// 	HttpHeader::iterator iter;
// 	for (iter = res->header.begin(); iter != res->header.end(); ++iter) {
// 		if (!iter->second.empty())data += iter->first + ": " + iter->second + "\r\n";
// 	}
// 	data += "\r\n" + res->content;
// 	return data;
// }

static int http_request_parse(HttpRequest* req, char* ip, size_t ipsz, int *port, PROTOCOL* protocol, const char* method, const char* url, HttpHeader* header, const char* content) {
	const char *m,*n;
	if (strncmp(url, "https://", 8) == 0) {
#ifdef OPESSL_SUPPORT
		*protocol = SSL_PROTOCOL;
		port = 443;
		url += 8;
#else
		return -1;
#endif
	}
	else if (strncmp(url, "http://", 7) == 0) {
		url += 7;
	}
	req->method = method;
	req->version = "HTTP/1.1";
	if (content) req->content = content;
	if (header) req->header = *header;
	m = strchr(url, '/');
	if (m) {
		req->uri = m;
	}
	else {
		req->uri = "/";
	}
	n = strchr(url, ':');
	if (n && (m == NULL || n < m)) {
		req->header["Host"] = std::string(url, n - url);
		*port = atoi(n + 1);
	}
	else if (m) {
		req->header["Host"] = std::string(url, m - url);
	}
	else {
		req->header["Host"] = std::string(url);
	}
	GetHostByName(req->header["Host"].c_str(), ip, ipsz);
	return 0;
}

int http_client_driver_regist() {
	HttpClientDriver* d = NULL;
	for (int i = 0; i <= ActorThreadWorker; ++i){
		d = new HttpClientDriver();
		d->thread_set(i);
		d->auto_release(false);
		d->task([d]() {
			HttpClient = d;
		});
	}
	main_http_client_driver = d;
	return 0;
}

int http_request(BaseWorker* worker, const char* method, const char* url, HttpHeader* header, const char* content, HTTP_CALLBACK callback){
	HttpTask* task = HttpClient->request(worker, method, url, header, content, callback);
	return task? 0: 1;
}

HttpTask* HttpClientDriver::request(BaseWorker* worker, const char* method, const char* url, HttpHeader* header, const char* content, HTTP_CALLBACK callback) {
	char ip[40];
	int port = 80, ret;
	PROTOCOL protocol = TCP_PROTOCOL;
	HSOCKET hsock;

	HttpRequest* req = new HttpRequest;
	HttpResponse* res = new HttpResponse;
	HttpTask* http_task = new HttpTask;
	if (!req || !res || !http_task) {
		goto error;
	}
	http_task->request = req;
	http_task->response = res;
	http_task->call = callback;
	http_task->worker = worker;
	http_task->user_data = NULL;

	ret = http_request_parse(req, ip, sizeof(ip), &port, &protocol, method, url, header, content);
	if (ret) goto error;

	hsock = HsocketConnect(this ? this : main_http_client_driver, ip, port, protocol);
	hsock->user_ptr = http_task;
	worker->ref_count++;
	return http_task;
error:
	if (req) delete req;
	if (res) delete res;
	if (http_task) delete http_task;
	return NULL;
}

static void http_response_callback(HttpTask* task, BaseWorker* worker, HttpResponse* res, int state){
	HTTP_RESFUNC* call = (HTTP_RESFUNC*)task->user_data;
	(*call)(*res, state);
	delete call;
}

HttpTask* HttpClientDriver::request(BaseWorker* worker, const char* method, const char* url, HttpHeader* header, const char* content, std::function<void(HttpResponse&, int stat)> func) {
	HTTP_RESFUNC* call = new HTTP_RESFUNC(func);
	HttpTask* task = this->request(worker, method, url, header, content, http_response_callback);
	if (!task) delete call;
	else task->user_data = call;
	return task;
}

void HttpClientDriver::ConnectionMade(HSOCKET hsock, PROTOCOL protocol) {
	HttpTask* http_task = (HttpTask*)hsock->user_ptr;
	std::string strem = http_request_encode(http_task->request);
	HsocketSend(hsock, strem.c_str(), int(strem.size()));
}
void HttpClientDriver::ConnectionFailed(HSOCKET hsock, int err) {
	HttpTask* http_task = (HttpTask*)hsock->user_ptr;
	BaseWorker* worker = http_task->worker;

	HTTP_CALLBACK callback = (HTTP_CALLBACK)http_task->call;
	callback(http_task, worker, http_task->response, State_Connection_Faile);

	delete http_task->request;
	delete http_task->response;
	delete http_task;
	worker->ref_count--;
	worker->check_release();
}
void HttpClientDriver::ConnectionClosed(HSOCKET hsock, int err) {
	HttpTask* http_task = (HttpTask*)hsock->user_ptr;
	BaseWorker* worker = http_task->worker;

	HTTP_CALLBACK callback = (HTTP_CALLBACK)http_task->call;
	callback(http_task, worker, http_task->response, State_Connection_Close);

	delete http_task->request;
	delete http_task->response;
	delete http_task;
	worker->ref_count--;
	worker->check_release();
}
void HttpClientDriver::ConnectionRecved(HSOCKET hsock, const char* data, int len) {
	HttpTask* http_task = (HttpTask*)hsock->user_ptr;
	int ret = http_response_decode(http_task->response, (char*)data, len);
	if (ret) {
		BaseWorker* worker = http_task->worker;

		HTTP_CALLBACK callback = (HTTP_CALLBACK)http_task->call;
		callback(http_task, worker, http_task->response, State_OK);

		delete http_task->request;
		delete http_task->response;
		delete http_task;
		HsocketPopBuf(hsock, ret);
		HsocketClosed(hsock);
		worker->ref_count--;
		worker->check_release();
	} 
}